package org.zizi.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.zizi.db.charset.CharsetEncoder;
import org.zizi.db.plan.ResultPlan;
import org.zizi.db.plan.ResultReader;
import org.zizi.db.tool.EasySqlTool;
import org.zizi.db.tool.SqlHelper;
import org.zizi.util.Closer;

public abstract class AbstractEasySql implements EasySql
{
	protected static final Logger log = Logger.getLogger(AbstractEasySql.class);
	
	protected ThreadLocal<DataSource> sources = new ThreadLocal<DataSource>();
	protected ThreadLocal<Connection> connections = new ThreadLocal<Connection>(); // 线程局部的数据库连接
	protected ThreadLocal<Integer> isolations = new ThreadLocal<Integer>(); // 事务隔离度
	protected ThreadLocal<Integer> depthes = new ThreadLocal<Integer>(); // 事务嵌套深度
	
	protected SqlHelper helper = new SqlHelper(this);
	protected CharsetEncoder charset = new CharsetEncoder();
	
	protected ResultPlan plan;
	protected EasySqlTool tool;
	
	public Connection getConnection() throws Exception
	{
		Integer i = isolations.get();
		if (i == null) throw new IllegalStateException("事务未开始，请先调用begin开始事务");
		
		Connection c = connections.get();
		if (c != null) return c;
		
		log.trace("开始事务");
		
		DataSource ds = sources.get();
		c = ds.getConnection();
		c.setTransactionIsolation(i);
		c.setAutoCommit(false);
		
		connections.set(c);
		
		return c;
	}
	
	public void begin(int isolation, DataSource source) throws Exception
	{
		Integer d = depthes.get();
		if (d == null) d = 0;
		
		depthes.set(d + 1);
		
		if (d == 0)
		{
			isolations.set(isolation);
			sources.set(source);
		}
	}

	public boolean commit() throws SQLException
	{
		Connection c = connections.get();
		if (c == null) return false;
		
		log.trace("提交事务");
		
		c.commit();
		Closer.close(c);
		
		connections.remove();
		
		return false;
	}

	public boolean end()
	{
		Integer d = depthes.get();
		if (d == null) return false;	//未调用begin
		
		if (d > 1)
		{
			depthes.set(d - 1);
			return false;				//未回到顶层，不处理
		}
		
		depthes.remove();
		isolations.remove();
		sources.remove();
		
		Connection c = connections.get();	//未真正开始事务，或已经提交，返回
		if (c == null) return true;
		
		connections.remove();
		
		log.trace("回滚事务");
		
		try
		{
			c.rollback();
		}
		catch (Throwable e)
		{
		}
		finally
		{
			Closer.close(c);
		}
		
		return true;
	}

	public void setIsolation(int isolation) throws Exception
	{
		log.trace("设置事务隔离度:" + isolation);
		
		isolations.set(isolation);
		
		Connection c = connections.get();
		if (c != null) c.setTransactionIsolation(isolation);
	}
	
	public EasySqlTool tool()
	{
		return tool;
	}
	
	public SqlHelper helper()
	{
		return helper;
	}
	
	public CharsetEncoder charset()
	{
		return charset;
	}

	public <T> T query(Class<T> type, String sql, Object... parameters) throws Exception
	{
		log.debug("单行查询：" + sql);
		
		Connection c = getConnection();
		
		PreparedStatement ps = null;
		ResultSet rs = null;
		try
		{
			ps = prepare(c, sql, parameters);
			rs = ps.executeQuery();
			
			if (rs.next())
				return build(type, rs);
			else
				return null; 
		}
		finally
		{
			Closer.close(rs);
			Closer.close(ps);
		}
	}

	public <T> List<T> list(Class<T> type, String sql, Object... parameters) throws Exception
	{
		log.debug("多行查询：" + sql);
		
		Connection c = getConnection();
		
		PreparedStatement ps = null;
		ResultSet rs = null;
		try
		{
			ps = prepare(c, sql, parameters);
			rs = ps.executeQuery();
		
			List<T> list = new ArrayList<T>();
			while (rs.next()) list.add(build(type, rs));
			
			return list;
		}
		finally
		{
			Closer.close(rs);
			Closer.close(ps);
		}
	}

	public int update(String sql, Object... parameters) throws Exception
	{
		log.debug("更新查询: " + sql);

		Connection c = getConnection();
		PreparedStatement ps = null;
		try
		{
			ps = prepare(c, sql, parameters);
			return ps.executeUpdate();
		}
		finally
		{
			Closer.close(ps);
		}
	}
	
	public abstract String getTableInfoSql(String table);
	
	////////////////////////
	
	protected abstract void setParameters(PreparedStatement ps, Object... parameters) throws Exception;
	
	protected <T> T build(Class<T> type, ResultSet rs) throws Exception
	{
		ResultReader<T> reader = plan.getResultReader(type, rs.getMetaData());
		return reader.build(rs);
	}
	
	protected PreparedStatement prepare(Connection connection, String sql, Object... parameters) throws Exception
	{
		PreparedStatement ps = null;
		
		try
		{
			ps = connection.prepareStatement(sql);
			setParameters(ps, parameters);
			
			return ps;
		}
		catch (Throwable e) 
		{
			Closer.close(ps);
			throw new Exception(e);
		}
	}
}
